# coding=utf-8

import StringIO
import os

class AutomataBuilder():

    def __init__(self):
        self.s = None
        self.states = []
        self.final = []
        self.delta = {}

    def add_state(self, q):
        self.states.append(q)

    def set_initial(self, q):
        self.s = q

    def add_final(self, q):
        self.final.append(q)

    def add_transition(self, q_i, c, q_f):
        if c == '':
            if not (q_i, c) in self.delta.keys():
                self.delta[(q_i, c)] = []
            self.delta[(q_i, c)].append(q_f)
        else:
            self.delta[(q_i, c)] = q_f


    def generate_diagram(self, filename):

        f = open(str(filename) + '.dot', 'w')
        f.write('digraph finite_state_machine {\n')
        f.write('rankdir=LR;\n')
        f.write('size="8,5"\n')
        
        f.write('node [shape = doublecircle]; ')
        [ f.write( str(n) + ' ') for n in self.final ]
        if len(self.final) != 0:
            f.write(';\n')
        
        f.write('node [shape = circle];\n')
        
        f.write('_pre_ [style = invis, height = 0, width = 0 ];\n')
        f.write('_pre_ -> "' + str(self.s) + '";\n')
        
        for k,v in self.delta.items():
            if k[1] == '':
                for q in v:
                    f.write('"' + str(k[0]) + '" -> "' + str(q) + '" [ label = "' + 'lambda' + '" ];\n')
            else:
                f.write('"' + str(k[0]) + '" -> "' + str(v) + '" [ label = "' + str(k[1]) + '" ];\n')

        f.write('}\n')
        
        f.close()

        #os.system('dot -Tpng ' + str(filename) + '.dot > ' + str(filename) + '.png')
        os.system('dot -Tsvg ' + str(filename) + '.dot > ' + str(filename) + '.svg')
        #os.system('dot -Tps ' + str(filename) + '.dot > ' + str(filename) + '.ps')
        

class AutomataChar(AutomataBuilder):
    def __init__(self, c, id_a):
        AutomataBuilder.__init__(self)

        id_a = str(id_a).strip()
        i = "Ei" + id_a
        f = "Ef" + id_a

        self.add_state(i)
        self.set_initial(i)
        
        self.add_state(f)
        self.add_final(f)
        
        self.add_transition(i, c, f)

class AutomataConcat(AutomataBuilder):
    def __init__(self, pre, pos, id_a):
        AutomataBuilder.__init__(self)

        if len(pre.final) != 1:
            print "Mal formado"
            
        if len(pos.final) != 1:
            print "Mal formado"

        # Mis estados son la unión de pre y post menos el final de pre
        # (podría ser el inicial de pos también...)
        self.states = pre.states[:]
        self.states.extend(pos.states)
        self.states.remove(pre.final[0])

        self.set_initial(pre.s)
        self.add_final(pos.final[0])

        # Todas las transiciones de pos lo son de self, excepto las que salen
        # del inicial de pos, que pasan a salir del final de pre...
	# FAQ: Claro papurri, todo joya pero te falta la vuelta, todos los que vuelven al inicial también.
        new_items = []
        for k,v in pos.delta.items():
	    if type(v)==list:
		if v.count(pos.s)>0:
		    v.remove(pos.s)
		    v.insert(0, pre.final[0])
	    elif v==pos.s:
		v = pre.final[0]
		
            if k[0] == pos.s:
                new_items.append(((pre.final[0], k[1]), v))
	    else:
                new_items.append((k,v))

        # Todas las transiciones de pre van en self también
        new_items.extend(pre.delta.items())
        self.delta = dict(new_items)


class AutomataStar(AutomataBuilder):

    def __init__(self, a, id_a):
        AutomataBuilder.__init__(self)

        if len(a.final) != 1:
            print "Mal formado"

        id_a = str(id_a).strip()
        i = "Ei" + id_a
        f = "Ef" + id_a

        self.states = a.states[:]

        self.add_state(i)
        self.set_initial(i)

        self.add_state(f)
        self.add_final(f)

        self.delta = dict(a.delta.items())
        self.add_transition(i, '', a.s)
        self.add_transition(i, '', f)
        self.add_transition(a.final[0], '', f)
        self.add_transition(a.final[0], '', a.s)


class AutomataUnion(AutomataBuilder):

    def __init__(self, a, b, id_a):
        AutomataBuilder.__init__(self)

        if len(a.final) != 1:
            print "Mal formado"
        if len(b.final) != 1:
            print "Mal formado"
        
        id_a = str(id_a).strip()
        i = "Ei" + id_a
        f = "Ef" + id_a

        self.states = a.states[:]
        self.states.extend(b.states)
        self.add_state(i)
        self.add_state(f)

        self.set_initial(i)
        self.add_final(f)

        new_items = a.delta.items()
        new_items.extend(b.delta.items())
        self.delta = dict(new_items)

        self.add_transition(i, '', a.s)
        self.add_transition(i, '', b.s)
        self.add_transition(a.final[0], '', f)
        self.add_transition(b.final[0], '', f)


class AutomataOptional(AutomataBuilder):
    def __init__(self, a, id_a):
        yo = AutomataUnion(AutomataVacio(id_a), a, id_a + 1)
        
        self.s = yo.s
        self.states = yo.states
        self.final = yo.final
        self.delta = yo.delta


class AutomataMas(AutomataBuilder):
    def __init__(self, a, id_a):
        # Esto no genera tantos estados nuevos como quizás debería, pero creo que anda.
        # Si a es q0 -t-> q1 -r-> q2, con q0 inicial y q2 final, esto generaría un autómata algo así:
        # q0 -t-> q1 -r-> q2 -lambda-> q3, y q2 -lambda-> q0, con q0 inicial y q3 final.
        # Esto pasa porque los estados de a y de a* son los mismos, excepto los que agrega a*.
        yo = AutomataConcat(a, AutomataStar(a, id_a), id_a + 1)

        self.s = yo.s
        self.states = yo.states
        self.final = yo.final
        self.delta = yo.delta

        
class AutomataVacio(AutomataBuilder):
    def __init__(self, id_a):
        AutomataBuilder.__init__(self)

        id_a = str(id_a).strip()
        i = "Ei" + id_a
        f = "Ef" + id_a

        self.add_state(i)
        self.set_initial(i)
        self.add_state(f)
        self.add_final(f)
        self.add_transition(i, '', f)

class AutomataDot(AutomataBuilder):
    def __init__(self, id_a):
        AutomataBuilder.__init__(self)

        id_a = str(id_a).strip()
        i = "Ei" + id_a
        f = "Ef" + id_a

        self.add_state(i)
        self.set_initial(i)
        self.add_state(f)
        self.add_final(f)
       
	# FAQ: Para que sea visible 
#        for j in range(ord('a'), ord('f')+1):      
        for j in range(ord('a'), ord('z')+1):      
            self.add_transition(i, chr(j), f)

        for j in range(ord('A'), ord('Z')+1):      
            self.add_transition(i, chr(j), f)

        for j in range(0, 9+1):      
            self.add_transition(i, j, f)
        
        self.add_transition(i, " ", f)


